
#include <openssl/objects.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/asn1.h>
#include <string.h>
#include <stdio.h>
#include <openssl/pem.h>
#include <fstream>
#include <openssl/sm3.h>
#include <openssl/sm2.h>
#include <openssl/ec.h>
// 从 PEM 文件中读取证书并获取签名值
void getSignatureValueFromPEM(const char *pemFileName,unsigned char **sig,int * siglen) {
    FILE *file = fopen(pemFileName, "r");
    if (!file) {
        fprintf(stderr, "Error opening file: %s\n", pemFileName);
        return;
    }

    // 从 PEM 文件中读取证书
    X509 *certificate = PEM_read_X509(file, nullptr, nullptr, nullptr);
    fclose(file);

    if (!certificate) {
        fprintf(stderr, "Error reading certificate from PEM file\n");
        return;
    }
 
    // 获取证书的签名值
    const ASN1_BIT_STRING *signatureData ;
    X509_get0_signature(&signatureData,nullptr,certificate);
    unsigned char *tmpdata = (unsigned char*)OPENSSL_malloc(signatureData->length);
    memcpy(tmpdata,signatureData->data,signatureData->length);
    *sig = tmpdata;
    *siglen = signatureData->length;
    // 打印签名数据
    printf("Certificate Signature Value ================ \r\n ");
    for (int i = 0; i < signatureData->length; ++i) {
        printf("%02X ", signatureData->data[i]);
    }
    printf("\n");
    // 释放证书资源
    X509_free(certificate);
}
// 从 PEM 文件中读取证书并获取签名数据
void getSignatureDataFromPEM(const char *pemFileName,unsigned char **data,int * datalen) {
    FILE *file = fopen(pemFileName, "r");
    if (!file) {
        fprintf(stderr, "Error opening file: %s\n", pemFileName);
        return;
    }

    // 从 PEM 文件中读取证书
    X509 *certificate = PEM_read_X509(file, nullptr, nullptr, nullptr);
   
    if (!certificate) {
        fprintf(stderr, "Error reading certificate from PEM file\n");
        return;
    }
    unsigned char* tbs_data =nullptr;
    // 获取证书的信息，不包含
    int certData_lenght= i2d_re_X509_tbs(certificate,&tbs_data);

    if(!tbs_data){
        fprintf(stderr, "Error reading tbs_data from certificate\n");
        return ;
    }
    unsigned char *tmpdata =  (unsigned char*)OPENSSL_malloc(certData_lenght);
    memcpy(tmpdata,tbs_data,certData_lenght);
    *datalen = certData_lenght;
    *data = tmpdata;
    // // 打印签名数据
    printf("Certificate Signature Data==============\r\n ");
    for (int i = 0; i <certData_lenght; ++i) {
         printf("%02X ", tbs_data[i]);
    }
    printf("\n");

    OPENSSL_free(tbs_data);
    // // 释放证书资源
    X509_free(certificate);
    fclose(file);
    
}

EVP_PKEY * getPublicKeyFromCertPem(const char *pemFileName){

    FILE *fp = fopen(pemFileName, "rb+");
    if (nullptr == fp) {
        return nullptr;
    }
    // 解析证书
    X509 *cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);

    if (nullptr == cert) {
        fclose(fp);
        return nullptr;
    }
    // 获取证书的签名算法ID
    int nid = X509_get_signature_nid(cert);

    // 提取证书的公钥
    EVP_PKEY *pkey = X509_get_pubkey(cert);
    const char* filePath = "./tmp_public_key.pem";
    FILE* file = fopen(filePath, "wb");
    // 将 EVP_PKEY 对象编码为 DER 格式
    PEM_write_PUBKEY(file, pkey);

    // 关闭文件
    fclose(file);
    // 输出公钥二进制数据
    int key_length = i2d_PUBKEY(pkey, nullptr);

      if (key_length <= 0) {
          fprintf(stderr, "Error getting public key length\n");
           return nullptr;

      }

      // 分配内存来存储公钥的二进制数据
      unsigned char *key_buffer = (unsigned char *)OPENSSL_malloc(key_length);
      if (!key_buffer) {
          fprintf(stderr, "Error allocating memory for public key\n");
          return nullptr;
      }

      // 将公钥数据写入缓冲区
      unsigned char *p = key_buffer;
      i2d_PUBKEY(pkey, &p);
       printf("public  key Data ================== \r\n");
      for (int i = 0; i < key_length; ++i) {
       printf("%02X ", key_buffer[i]);
      }
     printf("\n");
     OPENSSL_free(key_buffer);
    if (nullptr == pkey) {
        fclose(fp);
        X509_free(cert);
        return nullptr;
    }

    return pkey;
}

EVP_PKEY * getPublicKeyFromfile(const char *pemFileName){

    FILE *fp = fopen(pemFileName, "rb+");
    if (nullptr == fp) {
        return nullptr;
    }
    EVP_PKEY* public_key = PEM_read_PUBKEY(fp, nullptr,nullptr,nullptr);
    // 输出公钥二进制数据
    int key_length = i2d_PUBKEY(public_key, nullptr);

      if (key_length <= 0) {
          fprintf(stderr, "Error getting public key length\n");
           return nullptr;

      }

      // 分配内存来存储公钥的二进制数据
      unsigned char *key_buffer = (unsigned char *)OPENSSL_malloc(key_length);
      if (!key_buffer) {
          fprintf(stderr, "Error allocating memory for public key\n");
          return nullptr;
      }

      // 将公钥数据写入缓冲区
      unsigned char *p = key_buffer;
      i2d_PUBKEY(public_key, &p);
       printf("public  key Data ================== \r\n");
      for (int i = 0; i < key_length; ++i) {
       printf("%02X ", key_buffer[i]);
      }
     printf("\n");
    return public_key;
}

bool doSM2Sign(const unsigned char* data,const int datalen,const unsigned char* sig,const int siglen,EVP_PKEY* public_key){
    EVP_MD_CTX *cmd_ctx = EVP_MD_CTX_new();
    EVP_PKEY_CTX* pctx = nullptr;  // Owned by |ctx|.
    const EVP_MD *digest = EVP_sm3();
    if (!EVP_DigestVerifyInit(cmd_ctx, &pctx, digest, nullptr, public_key))
    {
     printf("EVP_DigestVerifyInit failed ;\r\n");
     return false;
    }


  if (!EVP_DigestVerifyUpdate(cmd_ctx, data, datalen)) {
    printf("EVP_DigestVerifyUpdate failed ;\r\n");
    return false;
  }

  bool ret = EVP_DigestVerifyFinal(cmd_ctx, sig,siglen);

  if (!ret) {
         printf("EVP_DigestVerifyFinal failed %lu;\n",ERR_get_error());
         ERR_print_errors_fp(stderr);  // 打印错误信息
     }
    EVP_MD_CTX_free(cmd_ctx);
    return ret;
}

int main(void)
{
    unsigned char* sig = nullptr;
    unsigned char* data = nullptr;
    int siglen =0;
    int datalen = 0;
    EVP_PKEY * pKey = nullptr;
    getSignatureValueFromPEM("../sm2only.ovssl.cn",&sig,&siglen);
    getSignatureDataFromPEM("../sm2only.ovssl.cn",&data,&datalen);
    pKey = getPublicKeyFromCertPem("../国密SM2服务器根证书V3");
   // pKey = getPublicKeyFromCertPem("/media/arv000/c19ee280-30c9-43bc-a890-05e8c96984bc/00-work/01-bug/证书验证问题/国密SM2服务器根证书V3_client.pem");
    if(nullptr == pKey){
        printf("get public key from cert(pem) failed. ");
        return -1;
    }
    bool ret = doSM2Sign(data,datalen,sig,siglen,pKey);
    printf("ret = %d\r\n",ret);
    OPENSSL_free(sig);
    OPENSSL_free(data);
}
